package com.hlkj.warboot.configurer.util;

import com.alibaba.fastjson.JSONObject;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.text.MessageFormat;
import java.util.Date;
import java.util.UUID;

/**
 * @Description: JSON WEB TOKEN
 * @Author: 李星 lixing_java@163.com
 * @Date: 2020/3/4 16:25
 *
 * JWT是一种安全标准。基本思路就是用户提供用户名和密码给认证服务器，服务器验证用户提交信息信息的合法性；
 * 如果验证成功，会产生并返回一个Token（令牌），用户可以使用这个token访问服务器上受保护的资源。
 *  第一部分：头部（header） {'typ':'JWT',//声明类型，这里是jwt'alg':'HS256'//声明加密的算法，通常直接使用HMACSHA256或RSA}
 *  第二部分：载荷（payload，该token里携带的有效信息
 *  第三部分：签名（signature）
 *
 * OAuth简单说就是一种授权的协议，只要授权方和被授权方遵守这个协议去写代码提供服务，那双方就是实现了OAuth模式
 *  你想登录豆瓣去看看电影评论，但你丫的从来没注册过豆瓣账号，又不想新注册一个再使用豆瓣，怎么办呢？不用担心，豆瓣已经为你这种懒人做了准备，用你的qq号可以授权给豆瓣进行登录
 *  具体思路可以参考微信H5页面的静默授权
 */
public class JwtUtil {
    private static final long startExpires = 0; // 默认 0 秒后生效
    private static final long endExpires = 600000; // 默认 1 分钟后过期
    private static final String SECRET = "25385a3d-2394-413d-a920-03e0bf25b81b"; // HS256 密钥

    public static void main(String[] argd) {

        JSONObject authData = new JSONObject();
        authData.put("account","admin");
        authData.put("password","123456");

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("startExpires", 1000); //
        jsonObject.put("endExpires", 3600000); //
        jsonObject.put("authData", authData.toJSONString());

        String jwt = createJwtForHS256(jsonObject);

        JSONObject jsonObject1 = verifyJwtForHS256(jwt);
        System.out.println(jsonObject1.getJSONObject("data"));
    }

    /*
     * @Description 以 HS256 的方式创建TOKEN
     * @Author 李星
     * @Date 2020/4/3 13:35
     * @Param [jsonObjectParam]
     * @return java.lang.String
     *
     * JSONObject authData = new JSONObject();
     * authData.put("account","admin");
     * authData.put("password","123456");
     *
     * JSONObject jsonObject = new JSONObject();
     * jsonObject.put("startExpires", 10000); //
     * jsonObject.put("endExpires", 3600000); //
     * jsonObject.put("authData", authData.toJSONString());
     * String jwt = createJwtForHMAC(jsonObject);
     */
    public static String createJwtForHS256(JSONObject jsonObjectParam) {
        if (jsonObjectParam == null) {
            LoggerUtil.error("createJwtForHMAC 创建token失败：入参为null");
            return null;
        }
        String token = null;
        try {
            // 创建时间
            long nowMillis = System.currentTimeMillis();
            Date createDate = new Date(nowMillis);
            // 生效时间
            long startExpires_ = startExpires;
            Date startDate;
            if (StrUtil.isEffective(jsonObjectParam.getString("startExpires"))) {
                startExpires_ = Long.parseLong(jsonObjectParam.getString("startExpires"));
            }
            startDate = new Date(nowMillis + startExpires_);
            // 过期时间
            long endExpires_ = endExpires;
            Date endDate;
            if (StrUtil.isEffective(jsonObjectParam.getString("endExpires"))) {
                endExpires_ = Long.parseLong(jsonObjectParam.getString("endExpires"));
            }
            endDate = new Date(nowMillis + startExpires_ + endExpires_);
            // 用户的授权信息
            String authData = jsonObjectParam.getString("authData");
            // 验证和签署算法
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            token = JWT.create()
                    .withJWTId(UUID.randomUUID().toString()) // 唯一身份标识，主要用来作为一次性token,从而回避重放攻击
                    .withIssuer("lixing_java@163.com") // 签发者
                    .withSubject("主题") // 主题
                    .withAudience("接收者") // 接收者
                    .withIssuedAt(createDate) // 签发时间
                    .withNotBefore(startDate) // 生效时间
                    .withExpiresAt(endDate) // 过期时间，这个过期时间必须要大于签发时间
                    .withClaim("authData", authData) // 登陆成功的用户数据
                    .sign(algorithm);
        } catch (JWTCreationException e) {
            LoggerUtil.error(MessageFormat.format("createJwtForHMAC 创建token失败：{0}", e.getMessage()));
        }
        return token;
    }

    /*
     * @Description  以 HS256 的方式解析TOKEN
     * @Author 李星
     * @Date 2020/4/3 13:54
     * @Param [jwt] 
     * @return com.alibaba.fastjson.JSONObject
     */
    public static JSONObject verifyJwtForHS256(String jwt) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET); // 验证和签署算法
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT decodedJWT = verifier.verify(jwt);
            //LoggerUtil.info("jwt-Token："+decodedJWT.getToken());
            //LoggerUtil.info("jwt-Header："+decodedJWT.getHeader()); // 头部信息，经过base64编码的JSON字符串（头部信息，经过base64编码的JSON字符串）
            //LoggerUtil.info("jwt-Payload："+decodedJWT.getPayload()); // 负载信息，经过base64编码的JSON字符串
            //LoggerUtil.info("jwt-Signature："+decodedJWT.getSignature()); // 签名
            Claim claim = decodedJWT.getClaim("authData");
            String authData = claim.asString();
            //Map<String, Claim> claims = decodedJWT.getClaims();
            //String authData = claims.get("authData").asString();
            return ResponseUtil.success(authData);
        } catch (TokenExpiredException e) {
            return ResponseUtil.fail("TOKEN超时");
        } catch (JWTVerificationException e) {
            return ResponseUtil.fail(e.getMessage());
        }
    }

}
